package com.idanchuang.component.consumer.ribbon;

import com.idanchuang.component.consumer.config.RibbonResolveMapConfig;
import com.idanchuang.component.core.helper.EnvContext;
import com.idanchuang.component.logback.helper.LogContext;
import com.idanchuang.sun.client.socket.discovery.AroundRule;
import com.netflix.loadbalancer.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Optional;

/**
 * @author yjy
 * @date 2019/12/25 18:02
 **/
public class DirectResolveAroundRule implements AroundRule {

    private static final Logger logger = LoggerFactory.getLogger(DirectResolveAroundRule.class);

    private final RibbonResolveMapConfig ribbonResolveMapConfig;

    public DirectResolveAroundRule(RibbonResolveMapConfig ribbonResolveMapConfig) {
        this.ribbonResolveMapConfig = ribbonResolveMapConfig;
    }

    @Override
    public Server preChoose(ILoadBalancer lb, Object o) {
        if (lb == null) {
            logger.warn("no load balancer");
            return null;
        }
        if (!(lb instanceof BaseLoadBalancer)) {
            return null;
        }
        String serviceName = ((BaseLoadBalancer) lb).getName();
        // 如果指定了特定实例
        String instance = EnvContext.getSpecialInstance(serviceName.toLowerCase());
        if (instance != null && !instance.isEmpty()) {
            Server server = makeServerByAddress(instance, 8080);
            if (ribbonResolveMapConfig.isLogChooseServer()) {
                logger.info("Choose special service instance [{}] > [{}]", serviceName, server);
            }
            return server;
        }
        // 尝试匹配直连配置
        Map<String, String> servers = ribbonResolveMapConfig.getDirectServers();
        if (servers == null || servers.isEmpty()) {
            return null;
        }
        for (Map.Entry<String, String> entry : servers.entrySet()) {
            if (entry.getKey().equalsIgnoreCase(serviceName)) {
                if (ribbonResolveMapConfig.isLogChooseServer()) {
                    logger.info("Choose direct resolve service provider [{}] > [{}]", serviceName, entry.getValue());
                }
                return makeServerByAddress(entry.getValue(), 80);
            }
        }
        return null;
    }

    /**
     * 根据address信息实例化Server
     * @param address ip:端口 或 ip
     * @param defaultPort 如果没有端口时, 默认的端口是啥?
     * @return ..
     */
    private static Server makeServerByAddress(String address, int defaultPort) {
        String[] arr = address.split(":");
        int port = arr.length == 1 ? defaultPort : Integer.parseInt(arr[1]);
        Server server = new Server(arr[0], port);
        server.setReadyToServe(true);
        server.setAlive(true);
        return server;
    }

    @Override
    public Server afterChoose(Server server, ILoadBalancer lb, Object o) {
        if (server != null) {
            return server;
        }
        if (lb == null) {
            logger.warn("no load balancer");
            return null;
        }
        return Optional.of(ribbonResolveMapConfig.isEnabled() && lb instanceof BaseLoadBalancer)
                .filter(Boolean::booleanValue)
                .flatMap(bool -> {
                    String serviceName = ((BaseLoadBalancer) lb).getName();
                    Map<String, String> servers = ribbonResolveMapConfig.getServers();
                    return servers.entrySet().stream()
                            .filter(entry -> entry.getKey().equalsIgnoreCase(serviceName))
                            .map(entry -> {
                                String[] address = entry.getValue().split(":");
                                int port = address.length == 1 ? 80 : Integer.parseInt(address[1]);
                                Server server1 = new Server(address[0], port);
                                server1.setReadyToServe(true);
                                server1.setAlive(true);
                                if (ribbonResolveMapConfig.isLogDegrade()) {
                                    LogContext.setKeyword("sunDegrade");
                                    logger.warn("Choose direct resolve service provider [{}] > [{}]", serviceName, server1);
                                }
                                return server1;
                            }).findFirst();
                })
                .orElse(null);
    }
}